#include "myFILE.h"
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>

// 为了防止和库中的函数冲突，模拟实现的接口均在名字前加下划线_
_FILE* _fopen(const char* filename, const char* mode)
{
    assert(filename && mode);

    int fmode = 0; 
    int fd = -1;
    // 判断用户以什么方式打开文件
    if (strcmp(mode, "w") == 0)
    {
        fmode = O_WRONLY | O_CREAT | O_TRUNC;
        fd = open(filename, fmode, 0666);
    }
    else if (strcmp(mode, "a") == 0)
    {
        fmode = O_WRONLY | O_CREAT | O_APPEND;
        fd = open(filename, fmode, 0666);
    }
    else if (strcmp(mode, "r") == 0)
    {
        fmode = O_RDONLY;
        fd = open(filename, fmode);
    }
    else // 打开文件失败  
    {
        return NULL;
    }
    // 检查系统调用open的返回值
    if (fd == -1)
    {
        return NULL;
    }
    // 来到此处说明打开文件成功
    // 对于_fopen接口，需要返回文件指针
    _FILE* fp = (_FILE*)malloc(sizeof(_FILE));
    assert(fp);
    
    // 设置打开文件的文件描述符，即设置为open的返回值即可
    fp->_fileno = fd;

    // 缓冲区设置
    fp->flag = FLUSH_ALL; //对于普通文件，默认情况会使用全缓冲来刷新缓冲区
    fp->out_pos = 0; // 还未写入数据，当前缓冲区大小为0
    fp->outbuffer = (char*)calloc(1024, sizeof(char)); // 默认缓冲区的大小为1024
     
    // calloc和malloc的区别是：calloc会将申请的空间全部初始化为0，而malloc不会
    assert(fp->outbuffer);

    return fp; 
}


size_t _fwrite(const char *ptr, size_t size, size_t nmemb, _FILE *stream)
{

    int writeRes = -1;
    size_t UserInputSize = size * nmemb;
    for (size_t i = 0; i < UserInputSize; i++)
    {
        // 在调用系统调用接口write之前一定是先将数据写入到缓冲区中
        stream->outbuffer[stream->out_pos] = ptr[i];
        stream->out_pos++;
        
        if (stream->out_pos == 1024) // 缓冲区满了
        {
            writeRes = write(stream->_fileno, stream->outbuffer, stream->out_pos);
            stream->out_pos = 0;
        }
        else if (stream->outbuffer[stream->out_pos - 1] == '\n')
        {
            writeRes = write(stream->_fileno, stream->outbuffer, stream->out_pos);
            stream->out_pos = 0;
        }
        else
        {
            continue;
        }

    }

    if(writeRes == -1) // 写入失败
    {
        return nmemb + 1; // 只要不等nmemb即可
    }
    else 
    {
        return nmemb;
    }
}

//int _fwrite(_FILE* fp, const char* s, size_t len)
//{
//    // 在调用write一定是先将数据写入缓冲区中，再满足一定条件再通过write写入到文件
//    memcpy(&fp->outbuffer[fp->out_pos], s, len);
//    fp->out_pos += len;
//    
//    if (fp->flag & FLUSH_NOW)
//    {
//        write(fp->_fileno, fp->outbuffer, fp->out_pos);
//        fp->out_pos = 0;
//    }
//    else if (fp->flag & FLUSH_LINE)
//    {
//        if (fp->outbuffer[fp->out_pos - 1] == '\n')
//        {
//            write(fp->_fileno, fp->outbuffer, fp->out_pos);
//            fp->out_pos = 0;
//        }
//        else 
//        {
//            return len;
//        }
//    }
//    else if (fp->flag & FLUSH_ALL)
//    {
//        if(fp->out_pos == 1024)
//        {
//            write(fp->_fileno,fp->outbuffer,fp->out_pos);
//            fp->out_pos = 0;
//        }
//        else 
//        {
//            return len;
//        }
//    }
//}



int _fflush(_FILE* stream) 
{
    // 如果缓冲区还有数据就刷新
    if (stream->out_pos > 0)
    {
        ssize_t writeRes = write(stream->_fileno, stream->outbuffer, stream->out_pos);
        if (writeRes == -1) // 写入失败
        {
            return writeRes;
        }
        stream->out_pos = 0;
    }
    return 0;
}

void _fclose(_FILE *fp)
{
    _fflush(fp);
    
    close(fp->_fileno);
    free(fp);
    fp = NULL;
}

// 用户在进行文件流操作时，实际要进行至少三次的拷贝：用户->用户级缓冲区->内核级缓冲区->文件，
// C语言 中众多文件流操作都是在完成 用户->用户级缓冲区 的这一次拷贝动作，其他语言也是如此，最终都是通过系统调用将数据冲刷到磁盘（文件）中
